﻿using System;
using TrainGame.Model;
using TrainGame.AI.TileLay;
using TrainGame.Common;
using TrainGame.Engine;

namespace TrainGame.AI
{
	[Serializable]
	public class AiPlayer : Player
	{
		public AiPersonality Personality;

		public AiPlayer(string sName)
			: base(sName)
		{
			Personality = new AiPersonality();
		}

		public override bool IsAiPlayer { get { return true; } }

		/// <summary>
		/// Delegate for reporting a text string describing the move the AI made (includes "AI couldn't find a move").
		/// Used for updating the UI.
		/// </summary>
		/// <param name="game">the Game in progress</param>
		/// <param name="player">the AI player making the move</param>
		/// <param name="moveDescription">text description of the move the AI made</param>
		public delegate void AiMoveDescriptionCallback (Game game, AiPlayer player, string moveDescription);

		/// <summary>
		/// Delegate for reporting the specific tile lay the AI made
		/// Used for updating the UI.
		/// </summary>
		/// <param name="game">the Game in progress</param>
		/// <param name="hex">the affected hex in Game.Gameboard.GameboardHexes</param>
		/// <param name="tile">the new tile applied to that hex</param>
		public delegate void AiTileLayCallback(Game game, AiPlayer player, GameboardHex hex, Tile tile);

		/// <summary>
		/// Process an AI Operating Round phase.  Not all phases are supported yet - returns false if didn't process a phase.
		/// </summary>
		/// <param name="game"></param>
		/// <param name="company"></param>
		/// <param name="moveDescriptionCallback">optional callback method for progress updates suitable for displaying on UI</param>
		/// <param name="tileLayCallback">optional callback method for a specific tile lay made, to drive updating the UI</param>
		/// <returns></returns>
		public bool ProcessAiORPhase(Game game, PublicCompany company,
			AiMoveDescriptionCallback moveDescriptionCallback, AiTileLayCallback tileLayCallback)
			// TODO: turn these callbacks into events?
		{
			bool TookAction;
			AiMoveConfig config = new AiMoveConfig(game, company, this);

			switch (game.OperatingRoundPhase)
			{
				case eOperatingRoundPhase.LayTile:
					TookAction = DoLayTile(config, moveDescriptionCallback, tileLayCallback);
					break;

				case eOperatingRoundPhase.PlaceToken:
					TookAction = DoPlaceToken(config, moveDescriptionCallback);
					break;

				case eOperatingRoundPhase.BuyTrains:
					TookAction = DoBuyTrains(config, moveDescriptionCallback);
					break;

				default:
					TookAction = false;
					break;
			}
			return TookAction;
		}

		#region AI tile lay methods

		private bool DoLayTile(AiMoveConfig config, AiMoveDescriptionCallback moveDescriptionCallback, AiTileLayCallback tileLayCallback)
		{
			bool TookAction = false;

			if (! config.Game.TileAlreadyPlayed)
			{
				AiTileLay tileLay;
				string AiGoal;
				bool FoundAction = ChooseTileLay(config, config.Game.Gameboard, out tileLay, out AiGoal);

				// Report the suggested tile lay
				string text;
				if (!FoundAction)
					text = string.Format("AI for {0} failed to find a tile lay", config.Company.Name);
				else
					text = string.Format("AI for {0} picks tile {1} at rotation {2} in hex {3} ({4})",
						config.Company.Name,
						tileLay.Tile.Name,
						tileLay.Rotation,
						tileLay.Hex.Location,
						AiGoal);
				XTrace.Trace("===> " + text);

				if (moveDescriptionCallback != null)
					moveDescriptionCallback(config.Game, this, text);

				if (FoundAction)
				{
					// Look up the hex on the real gameboard, since tile lay may have been developed on a copy
					GameboardHex hex = config.Game.Gameboard.GameboardHexes[tileLay.Hex.Location];
					if (hex != null)
					{
						TookAction = ApplyTileLay(config.Game, hex, tileLay);
						if (TookAction)
						{
							config.Game.TileAlreadyPlayed = true;

							if (tileLayCallback != null)
								tileLayCallback(config.Game, this, hex, tileLay.Tile);	// tell UI to update with new tile details
						}
					}
				}
				// try setting this even on ! FoundAction, otherwise AI tries it again on Done click
				config.Game.TileAlreadyPlayed = true;
			}
			return TookAction;
		}

		private bool ChooseTileLay(AiMoveConfig config, Gameboard gameboard, out AiTileLay tileLay, out string AiGoal)
		{
			tileLay = AiTilePicker.ChooseTileLay(config, gameboard, config.Game.Tiles, config.Company, out AiGoal /*, Personality*/);
			if (tileLay == null)
				return false;	// couldn't find a tile lay for some reason
			return true;
		}

		private bool ApplyTileLay(Game game, GameboardHex hex, AiTileLay tileLay)
		{
			Tile oldTile = hex.Tile;
			Tile newTile = tileLay.Tile;

			string sError = "";
			if (hex.Tile == null)
			{
				if (! hex.InitializeWithTile(game, tileLay.Tile, game.CurrentlyOperatingCompany, ref sError))
					return false;
				tileLay.Tile.QuantityLeft--;
			}
			else if (hex.CanUpgradeTo(game, tileLay.Tile, ref sError))
			{
				if (! hex.UpgradeTo(game, tileLay.Tile, ref sError))
					return false;
			}
			else if (hex.CanDowngradeTo(game, tileLay.Tile, ref sError))
			{
				if (! hex.DowngradeTo(game, tileLay.Tile, ref sError))
					return false;
			}

			if (!GameEngine.FinishTileLay(game, hex, tileLay.Rotation))
				return false;	// this is an especailly bad one, because it's already been half-applied above

#if not_needed	// not needed because UpgradeTo and DowngradeTo do this
			// adjust tile quantities
			if (oldTile != null)	// only non-null if it was an upgrade or downgrade
				oldTile.QuantityLeft++;
			newTile.QuantityLeft--;
#endif
			return true;
		}

		#endregion AI tile lay methods


		#region AI place token methods

		private bool DoPlaceToken(AiMoveConfig config, AiMoveDescriptionCallback moveDescriptionCallback)
		{
			bool TookAction = false;

			return TookAction;
		}

		#endregion AI place token methods


		#region AI train purchase methods

		private bool DoBuyTrains(AiMoveConfig config, AiMoveDescriptionCallback moveDescriptionCallback)
		{
			bool TookAction = false;

			return TookAction;
		}

		#endregion AI train purchase methods
	}
}
